package org.ntu.rtsearch.index.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author fuhao(email: hfhimage@gmail.com)         
 * @date 2011-7-27
 */
public class MutiIndex {
	
	private int maxTf;
	
	private int maxSig;
	
	private double upperBound;
	
	private double lowerBound;
	
	// save terms and their posting lists
	private Map<String, List<Posting>> postingListMap = new HashMap<String, List<Posting>>();

	// document info
	private Map<Integer, Document> docMap = new HashMap<Integer, Document>();

	// field info
	private Map<String, Integer> fieldMap = new HashMap<String, Integer>();
	
	private long postingCount = 0;
	
	private long sizeInByte = 0;

	/**
	 * get the field id of the field name
	 * @param fieldName
	 * @return
	 */
	public Integer getFieldNum(String fieldName) {
		Integer fieldNum = fieldMap.get(fieldName);
		if(fieldNum == null) {
			fieldNum = fieldMap.size();
			fieldMap.put(fieldName, fieldNum);
		}
		return fieldNum;
	}

	/**
	 * add posting to the inverted Index
	 * @param posting
	 */
	public synchronized void addPosting(String term, Posting posting) {
		List<Posting> postingList = getPostingListMap().get(term);

		if (postingList == null) {
			postingList = new ArrayList<Posting>();
			getPostingListMap().put(term, postingList);
			sizeInByte += (term.length() + 4);
		}

		postingList.add(posting);
		sizeInByte += (posting.getSizeInByte() + 4);
		postingCount++;
	}

	/**
	 * save the document infomation
	 * @param doc
	 */
	public void saveDocument(Document doc) {
		docMap.put(doc.getDocId(), doc);
	}
	
	/**
	 * show all term in inverted index
	 */
	public void showTermDict() {
		System.out.println("total term amount: " + getPostingListMap().size());
		int cnt = 0;
		for (String term : getPostingListMap().keySet()) {
			System.out.print(term + ", ");
			if((cnt++) % 10 == 0) 
				System.out.println("");
		}
	}
	
	/**
	 * show the inverted index
	 */
	public void showIndex() {
		System.out.println("term num: " + getPostingListMap().size());
		for (Map.Entry<String, List<Posting>> entry : getPostingListMap().entrySet()) {
			System.out.print(entry.getKey() + ": ");
			for (Posting posting : entry.getValue()) {
				System.out.print("" + posting.getDocId() + "-"
						+ posting.getTermFreq() + ", ");
			}
			System.out.println("");
		}
	}
	
	/**
	 * get posting list of the term
	 * @param term
	 * @return
	 */
	public List<Posting> getPostingList(String term) {
		return getPostingListMap().get(term);
	}

	/**
	 * get the document according to the doc id;
	 * @param docId
	 * @return
	 */
	public Document getDoc(int docId) {
		return docMap.get(docId);
	}


	/**
	 * merge two index, and return the merged index
	 * @param newIndex
	 * @param oldIndex
	 * @return
	 */
	public MutiIndex mergeWith(MutiIndex newIndex) {
		// merge postingListMap, merge the posting list of the same term
		Map<String, List<Posting>> newIdxPListMap = newIndex.getPostingListMap();
		List<Posting> newPList = null;
		List<Posting> oldPList = null;
		String term = null;
		// the newIndex has the fewer term, so use it as loop for optimization
		for (Map.Entry<String, List<Posting>> entry : newIdxPListMap.entrySet()) {
			term = entry.getKey();
			newPList = entry.getValue();
			oldPList = postingListMap.get(term);
			if(oldPList != null) {
				newPList.addAll(oldPList);
				sizeInByte -= (term.length() + 4);	// reduplicate , count twice, so subtract the reduplicate part
			}
			postingListMap.put(term, newPList);
		}
		
		// merge doc map, they have not intersection of doc id, 
		// so no overlap will happen, just use putAll
		docMap.putAll(newIndex.getDocMap());
		
		// merge fieldMap, for tweet, indexes have same fieldMap, 
		// so just do nothing here
		
		postingCount += newIndex.getSize();
		sizeInByte += newIndex.getSizeInByte();
		
		return this;
	}

	public long getSizeInByte() {
		return sizeInByte;
	}

	/**
	 * return the amount of posting
	 * @return
	 */
	public long getSize() {
		return postingCount;
	}

	public int getDocCount() {
		return docMap.size();
	}

	public Map<String, List<Posting>> getPostingListMap() {
		return postingListMap;
	}

	public Map<Integer, Document> getDocMap() {
		return docMap;
	}

}
